package com.example.demo.dao.base;

import com.example.demo.model.page.PageResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class BaseRepository<T> {

    static final Logger logger = LoggerFactory.getLogger(BaseRepository.class);

    static final Map<Class<?>, Function<Number, Number>> map = Map.of(
            Byte.class, Number::byteValue,
            Short.class, Number::shortValue,
            Integer.class, Number::intValue,
            Long.class, Number::longValue,
            Float.class, Number::floatValue,
            Double.class, Number::doubleValue
    );

    public final JdbcClient jdbcClient;

    private final Class<T> entityClass;

    private final String tableName;

    private final String idColumn;

    /**
     * id类型: 1-自增,null-自定义
     */
    private Integer idType;

    public BaseRepository(JdbcClient jdbcClient, Class<T> entityClass, String tableName) {
        this(jdbcClient, entityClass, tableName, "id", null);
    }

    public BaseRepository(JdbcClient jdbcClient, Class<T> entityClass, String tableName, Integer idType) {
        this(jdbcClient, entityClass, tableName, "id", idType);
    }

    public BaseRepository(JdbcClient jdbcClient, Class<T> entityClass, String tableName, String idColumn, Integer idType) {
        this.jdbcClient = jdbcClient;
        this.entityClass = entityClass;
        this.tableName = tableName;
        this.idColumn = idColumn;
        this.idType = idType;
    }

    /**
     * 添加
     *
     * @param entity entity
     */
    public int insert(T entity) {
        Map<String, Object> columnAndValueMap = getColumnAndValueMap(entity);
        String colSql = String.join(",", columnAndValueMap.keySet());
        String placeholders = columnAndValueMap.values().stream().map(v -> "?")
                .collect(Collectors.joining(","));
        String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, colSql, placeholders).trim();
        List<Object> values = new ArrayList<>(columnAndValueMap.values());
        logger.info("=====insert===={},{}", sql, values);
        int res = 0;
        // 自增id
        if(Objects.nonNull(idType) && idType.equals(1)) {
            KeyHolder keyHolder = new GeneratedKeyHolder();
            res = jdbcClient.sql(sql).params(values.toArray()).update(keyHolder);
            Number number = keyHolder.getKey();
            logger.info("key={}", number);
            Field field = ReflectionUtils.findField(entityClass, idColumn);
            if (Objects.isNull(field)) {
                throw new RuntimeException("自增id不存在");
            }
            field.setAccessible(true);
            try {
                Function<Number, Number> function = map.get(field.getType());
                if(Objects.nonNull(function)) {
                    field.set(entity, function.apply(number));
                } else {
                    field.set(entity, number);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("设置id自增值错误");
            }
        } else {
            res = jdbcClient.sql(sql).params(values).update();
        }
        return res;
    }

    /**
     * 删除
     *
     * @param id id
     */
    public int deleteById(Serializable id) {
        String sql = String.format("DELETE FROM %s WHERE %s = ?", tableName, idColumn).trim();
        logger.info("=====delete===={},{}", sql, id);
        return jdbcClient.sql(sql).param(id).update();
    }

    /**
     * 更新
     *
     * @param entity entity
     * @param id id
     */
    public int updateById(T entity, Serializable id) {
        Map<String, Object> columnAndValueMap = getColumnAndValueMap(entity);
        columnAndValueMap.remove(idColumn);
        String setSql = columnAndValueMap.keySet().stream().map(x -> x + " = ?").collect(Collectors.joining(","));
        String sql = String.format("UPDATE %s SET %s WHERE %s = ?", tableName, setSql, idColumn).trim();
        List<Object> values = new ArrayList<>(columnAndValueMap.values());
        values.add(id);
        logger.info("=====updateById===={},{}", sql, values);
        return jdbcClient.sql(sql).params(values).update();
    }

    /**
     * 更新
     *
     * @param entity entity
     * @param queryWrapper queryWrapper
     */
    public int update(T entity, QueryWrapper<T> queryWrapper) {
        Map<String, Object> columnAndValueMap = getColumnAndValueMap(entity);
        return this.update(columnAndValueMap, queryWrapper);
    }

    /**
     * 更新
     *
     * @param columnAndValueMap columnAndValueMap
     * @param queryWrapper queryWrapper
     */
    public int update(Map<String, Object> columnAndValueMap, QueryWrapper<T> queryWrapper) {
        String whereClause = queryWrapper.getWhereClause();
        if (!StringUtils.hasText(whereClause)) {
            throw new RuntimeException("更新条件不能为空");
        }
        columnAndValueMap.remove(idColumn);
        String setSql = columnAndValueMap.keySet().stream().map(x -> x + " = ?").collect(Collectors.joining(","));
        String sql = String.format("UPDATE %s SET %s %s", tableName, setSql, whereClause).trim();
        List<Object> values = new ArrayList<>(columnAndValueMap.values());
        values.add(queryWrapper.getParams());
        logger.info("=====update===={},{}", sql, values);
        return jdbcClient.sql(sql).params(values).update();
    }

    /**
     * 查询
     *
     * @param id id
     */
    public T selectById(Serializable id) {
        String sql = String.format("SELECT * FROM %s WHERE %s = ?", tableName, idColumn).trim();
        logger.info("=====selectById===={},{}", sql, id);
        return jdbcClient.sql(sql).param(id).query(entityClass).optional().orElse(null);
    }

    /**
     * 查询
     *
     * @param queryWrapper queryWrapper
     */
    public T selectByOne(QueryWrapper<T> queryWrapper) {
        String sql = String.format("SELECT * FROM %s %s %s limit 1", tableName,
                queryWrapper.getWhereClause(), queryWrapper.getOrderByClause()).trim();
        List<Object> params = queryWrapper.getParams();
        logger.info("=====selectByOne===={},{}", sql, params);
        return jdbcClient.sql(sql).params(params).query(entityClass).optional().orElse(null);
    }

    /**
     * 查询
     *
     * @param queryWrapper queryWrapper
     */
    public List<T> selectList(QueryWrapper<T> queryWrapper) {
        String sql = String.format("SELECT * FROM %s %s %s", tableName,
                queryWrapper.getWhereClause(),
                queryWrapper.getOrderByClause()).trim();
        List<Object> params = queryWrapper.getParams();
        logger.info("=====selectList===={},{}", sql, params);
        return jdbcClient.sql(sql).params(params).query(entityClass).list();
    }

    /**
     * 分页查询
     *
     * @param queryWrapper queryWrapper
     * @param pageNum pageNum
     * @param pageSize pageSize
     */
    public PageResult<T> selectPage(QueryWrapper<T> queryWrapper, int pageNum, int pageSize) {
        // 计算总记录数
        String whereClause = queryWrapper.getWhereClause();
        String countSql = String.format("SELECT COUNT(*) FROM %s %s",
                tableName, whereClause).trim();
        List<Object> params = queryWrapper.getParams();
        logger.info("=====selectPageCount===={},{}", countSql, params);
        long total = jdbcClient.sql(countSql)
                .params(params)
                .query(Long.class)
                .single();
        if (total <= 0){
            return new PageResult<>(total, new ArrayList<>(), pageNum, pageSize);
        }
        // 计算分页参数
        int offset = (pageNum - 1) * pageSize;
        // 分页查询
        String sql = String.format("SELECT * FROM %s %s %s LIMIT %d OFFSET %d",
                tableName, whereClause, queryWrapper.getOrderByClause(),
                pageSize, offset).trim();
        logger.info("=====selectPage===={},{}", sql, params);
        List<T> records = jdbcClient.sql(sql)
                .params(params).query(entityClass).list();
        return new PageResult<>(total, records, pageNum, pageSize);
    }

    /**
     * 获取column and value
     *
     * @param entity entity
     */
    private Map<String, Object> getColumnAndValueMap(T entity){
        Map<String, Object> map = new HashMap<>();
        if (entity == null) {
            return map;
        }
        ReflectionUtils.doWithFields(entityClass, f -> {
            f.setAccessible(true);
            Object val = f.get(entity);
            if (Objects.nonNull(val)) {
                map.put(LambdaUtils.camelToUnderline(f.getName()), val);
            }
        });
        return map;
    }
}